# -*- coding: utf-8 -*-

import time
import re
from selenium import webdriver
from selenium.webdriver import ActionChains
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.select import Select
from selenium.webdriver.support.wait import WebDriverWait

from tools.verify import Verify
from tools.decs import relations
from tools.Logger import path, logger


class Web:

    def __init__(self):
        """构造函数"""
        # 通过冒号指定类型，好处就是后面封装的时候，可以点出方法来
        self.driver: webdriver.Chrome = None

        # 关联字典
        self.relations_dict = {}

    def openbrowser(self, browser: str = 'gc'):
        """
        打开浏览器
        :param browser: gc（默认）-谷歌；ie-ie浏览器；ed-Edge浏览器
        :return:
        """
        if browser == 'ie':
            self.driver = webdriver.Ie()
        elif browser == 'ed':
            option = webdriver.EdgeOptions()
            # 不自动关闭浏览器的配置
            option.add_experimental_option("detach", True)
            # 在打开浏览器之前，去掉自动化标识
            option.add_experimental_option('excludeSwitches', ['enable-automation'])
            option.add_argument('--disable-blink-features=AutomationControlled')

            ## 关掉密码弹窗
            prefs = {}
            prefs['credentials_enable_service'] = False
            prefs['profile.password_manager_enabled'] = False
            option.add_experimental_option('prefs', prefs)

            # 使用初始化后的浏览器配置文件
            option.add_argument(r'--user-data-dir=D:\edge_dir1')

            self.driver = webdriver.Edge(options=option)
        else:
            option = webdriver.ChromeOptions()
            # 不自动关闭浏览器的配置
            option.add_experimental_option("detach", True)
            # 在打开浏览器之前，去掉自动化标识
            option.add_experimental_option('excludeSwitches', ['enable-automation'])
            option.add_argument('--disable-blink-features=AutomationControlled')

            ##关掉密码弹窗
            prefs = {}
            prefs['credentials_enable_service'] = False
            prefs['profile.password_manager_enabled'] = False
            option.add_experimental_option('prefs', prefs)

            self.driver = webdriver.Chrome(options=option)

        # 隐式等待
        self.driver.implicitly_wait(5)
        # 最大化
        self.driver.maximize_window()

    def geturl(self, url: str = ''):
        """
        打开网站
        :param url: 网站地址，一定要http开头
        :return:
        """
        # 这个是兼容非http开头的url写法
        if not url.startswith('http'):
            url = 'http://' + url

        self.driver.get(url)

    def __find_ele(self, lo: str = ''):
        """
        xpath一般是/ or (开头
        css一般是#开头或者包含>
        id一般是纯英文单词
        :param lo: 定位表达式，支持xpath，css，id
        :return: 返回找到元素，如果没找到就报错
        """
        if lo.startswith('/') or lo.startswith('('):
            ele = self.driver.find_element('xpath', lo)
        elif lo.startswith('#') or lo.__contains__('>'):
            ele = self.driver.find_element('css selector', lo)
        else:
            ele = self.driver.find_element('id', lo)

        # 操作元素高亮
        if ele:
            self.driver.execute_script("arguments[0].style.background = '#4aff00'",ele)

        return ele

    def gettitle(self):
        title = self.driver.title
        self.relations_dict['title'] = title
        return title

    @relations
    def input(self, lo: str = '', value: str = "", ):
        """
        往元素里面输入
        :param lo: 定位表达式，支持xpath，css，id
        :param value: 你要输入的值
        :return:
        """
        ele = self.__find_ele(lo)
        # 兼容png图片,上传
        if value.endswith('.png') or value.endswith('.jpg'):
            value = path + 'lib\\' + value

        ele.send_keys(value)

    def clear(self, lo: str = ''):
        ele = self.__find_ele(lo)
        ele.clear()

    def click(self, lo: str = ''):
        """
        点击元素
        :param lo: 定位表达式，支持xpath，css，id
        :return:
        """
        ele = self.__find_ele(lo)
        ele.click()

    def tryclicks(self, lo: str = ''):
        """
        尝试点击元素，适用于不一定出现的元素的操作
        :param lo: 定位表达式，支持xpath，css，id
        :return:
        """
        try:
            ele = self.__find_ele(lo)
            ele.click()
        except:
            pass

    def gettext(self, lo: str = '', reg: str = ''):
        """
        获取元素的文本
        :param lo: 定位表达式，支持xpath，css，id
        :param reg: 如果传了正则表达式，就把获取的文本，按正则处理
        :return : 关联字典里面会添加一个text的系统变量
        """
        ele = self.__find_ele(lo)
        text = ele.text
        print(text)
        if reg:
            res = re.findall(reg, text)
            # 如果正则匹配成功就返回第一个匹配值
            if res:
                text = res[0]

        # 存到关联里面
        self.relations_dict['text'] = text
        return text

    @relations
    def saveparam(self, key: str = '', value: str = ''):
        """
        往关联字典里面添加一个键值对
        :param key: 键
        :param value: 值
        :return:
        """
        self.relations_dict[key] = value

    def hover(self, lo: str = ''):
        """
        鼠标悬停到元素
        :param lo: 定位表达式，支持xpath，css，id
        :return:
        """
        ele = self.__find_ele(lo)
        action = ActionChains(self.driver)
        # .perform()，让前面的一系列鼠标操作生效
        action.move_to_element(ele).perform()

    def intoiframe(self, lo: str = ''):
        """
        进入iframe
        :param lo: 定位表达式，支持xpath，css，id
        :return:
        """
        iframe = self.__find_ele(lo)
        self.driver.switch_to.frame(iframe)

    def outiframe(self):
        """
        切到所有iframe外面
        :return:
        """
        self.driver.switch_to.default_content()

    def waitele(self, lo: str = '', attr: str = '', expect_value: str = ''):
        """
        等待元素的某个属性值是expect_value的时候
        :param lo: 定位表达式，支持xpath，css，id
        :param attr: 属性
        :param expect_value: 期望值
        :return:
        """
        if lo.startswith('/') or lo.startswith('('):
            ele = ('xpath', lo)
        elif lo.startswith('#') or lo.__contains__('>'):
            ele = ('css selector', lo)
        else:
            ele = ('id', lo)

        WebDriverWait(self.driver, 10, 0.5).until(
            EC.text_to_be_present_in_element_attribute(
                ele, attr, expect_value))

    def select(self, lo: str = '', value: str = ''):
        """
        下拉框选择
        :param lo: 定位表达式，支持xpath，css，id
        :param value: 选项（输入整数，按下标取；输入字符串，按visible_text取）
        :return:
        """
        ele = self.__find_ele(lo)
        se = Select(ele)
        try:
            # 如果是整数，就按下标取
            value = int(value)
            se.select_by_index(value)
        except:
            # 如果不是整数就按可见文本取
            se.select_by_visible_text(value)

    def sleep(self, t: str = '1'):
        """
        固定等待
        :param t: 等待时间，单位s
        :return:
        """
        # 尝试把传入的时间转成小数
        try:
            t = float(t)
        except:
            # 如果转失败，就说明输入的不是一个数字
            # 默认就按等待1s
            t = 1

        time.sleep(t)

    def switchwin(self, win: str = ''):
        """
        窗口切换
        :param win: 如果是数字，就按下标；如果是字符串就按title
        :return:
        """
        handles = self.driver.window_handles
        try:
            # 按下标
            win = int(win)
            try:
                self.driver.switch_to.window(handles[win])
            except IndexError:
                print('窗口下标越界')
        except:
            # 按title切换
            for h in handles:
                self.driver.switch_to.window(h)
                title = self.driver.title
                if title.__contains__('我的订单'):
                    break

    def getverify(self, lo1: str = '', lo2: str = '', lo3: str = ''):
        """
        获取验证码
        :param lo1: 验证码图片定位表达式，支持xpath，css，id
        :param lo2: 输入框定位表达式，支持xpath，css，id
        :param lo3: 触发按钮位表达式，支持xpath，css，id
        :return: 会获得图文验证码，关联里面也会增加一个verify系统变量
        """
        pic_ver = 'None'
        while True:
            try:
                self.driver.implicitly_wait(3)
                # 如果找到了验证码图片，就是没有登录成功
                ele = self.__find_ele(lo1)
                self.driver.implicitly_wait(5)
            except:
                # 如果没找到，就是登录成功了
                break

            ele.screenshot('./verify.png')
            verify = Verify('wuqingfqng', '6e8ebd2e301f3d5331e1e230ff3f3ca5', '904357')
            pic_ver = verify.PostPic(1902, './verify.png')
            print(pic_ver)
            self.relations_dict['verify'] = pic_ver

            # 输入
            self.input(lo2, pic_ver)
            # 登录
            self.click(lo3)

            time.sleep(2)

        return pic_ver

    def myslide(self, lo=""):
        """12306滑块"""
        ele = self.__find_ele(lo)
        action = ActionChains(self.driver)
        action.move_to_element(ele).click_and_hold().move_by_offset(500, 0).perform()

    @relations
    def assert_contains(self, act_res='', exp_res=''):
        """
        断言实际结果包含期望值
        :param act_res: 实际结果
        :param exp_res: 期望值
        :return: 成功True，失败就抛异常
        """
        logger.info(f'断言，实际结果：{act_res}   --- 期望值：{exp_res}')
        if act_res.__contains__(exp_res):
            return True
        else:
            assert act_res.__contains__(exp_res)

    def quit(self):
        """关闭浏览器"""
        self.driver.quit()
        self.driver = None
    #nxz 2024年7月25日16:34:14 add 获取元素的属性
    def get_attribute(self,locator,attr):
        attrstr = self.__find_element(locator).get_attribute(attr)
        return attrstr